// This file is part of the ustl library, an STL implementation.
//
// Copyright (C) 2005 by Mike Sharov <msharov@users.sourceforge.net>
// This file is free software, distributed under the MIT License.
//
// file.cc
//

#include "fstream.h"
#include "uexception.h"
#include "uutility.h"
#include <unistd.h>
#include <errno.h>

namespace ustl {

/// Default constructor.
fstream::fstream (void)
: ios_base (),
  m_Filename ()
{
	m_fs.Connect();
}

/// Opens \p filename in \p mode.
fstream::fstream (const char* filename, openmode mode)
: ios_base (),
  m_Filename ()
{
	m_fs.Connect();
    open (filename, mode);
}

/// Attaches to \p nfd of \p filename.
fstream::fstream (int nfd, const char* filename)
: ios_base (),
  m_Filename ()
{
	m_fs.Connect();
    attach (nfd, filename);
}

/// Destructor. Closes if still open, but without throwing.
fstream::~fstream (void) throw()
{
    clear (goodbit);
    exceptions (goodbit);
    close();
    assert (!(rdstate() & badbit) && "close failed in the destructor! This may lead to loss of user data. Please call close() manually and either enable exceptions or check the badbit.");
}

/// Sets state \p s and throws depending on the exception setting.
void fstream::set_and_throw (iostate s, const char* op)
{
    if (ios_base::set_and_throw (s))
	throw file_exception (op, name());
}

/// Attaches to the given \p nfd.
void fstream::attach (int nfd, const char* filename)
{
    assert (filename && "Don't do that");
    clear (goodbit);
    if (nfd < 0 && ios_base::set_and_throw (badbit))
	throw file_exception ("open", filename);
    close();
    m_fd.Adopt(m_fs, nfd);
    m_Filename = filename;
}

/// Detaches from the current fd.
void fstream::detach (void)
{
    m_fd.Close();
    m_Filename.clear();
}

/// \brief Opens \p filename in the given mode.
/// \warning The string at \p filename must exist until the object is closed.
void fstream::open (const char* filename, openmode mode, mode_t perms)
{
	auto_ptr<HBufC> buf;
   	buf	= string(filename).Buf();
    int err = m_fd.Open (m_fs, *buf, mode);
    if(err)
    	err = m_fd.Replace (m_fs, *buf, mode);
	if (err < 0)
		throw file_exception ("open", filename);
	(void)perms;
}

/// Closes the file and throws on error.
void fstream::close (void)
{
    detach();
}

/// Moves the current file position to \p n.
off_t fstream::seek (off_t n, seekdir whence) 
{
    int p = 0;
    int err = m_fd.Seek((TSeek)whence, p);
    if ((p < 0) || (err <0))
	set_and_throw (failbit, "seek");
    return (p);
}

/// Returns the current file position.
off_t fstream::pos (void) const
{
	int offset = 0;
    m_fd.Seek((TSeek)cur, offset);
	return offset;
}

/// Reads \p n bytes into \p p.
off_t fstream::read (void* p, off_t n)
{
    off_t br (0);
    while (br < n && good())
	br += readsome (advance (p, br), n - br);
    return (br);
}

/// Reads at most \p n bytes into \p p, stopping when it feels like it.
off_t fstream::readsome (void* p, off_t n)
{
    ssize_t brn;
	TPtr8 buf((TUint8*)(p), n);
    do { brn = m_fd.Read(buf); } while (brn < 0 && errno == EINTR);
    if (brn > 0)
	return (brn);
    if (brn < 0 && errno != EAGAIN)
	set_and_throw (failbit, "read");
    if (!brn && ios_base::set_and_throw (eofbit | failbit))
	throw stream_bounds_exception ("read", name(), pos(), n, 0);
    return (0);
}

/// Writes \p n bytes from \p p.
off_t fstream::write (const void* p, off_t n)
{
    off_t btw (n);
    while (btw) {
	const off_t bw (n - btw);
	TPtrC8 buf((TUint8*)advance(p,bw), (int)btw);
	int err = m_fd.Write(buf);
	ssize_t bwn = buf.Length();
	if (bwn > 0)
	    btw -= bwn;
	else if (!bwn) {
	    if (ios_base::set_and_throw (eofbit | failbit))
		throw stream_bounds_exception ("write", name(), pos() - bw, n, bw);
	    break;
	} else if (err) {
	    set_and_throw (failbit, "write");
	    break;
	}
    }
    return (n - btw);
}

/// Returns the file size.
off_t fstream::size (void) const
{
	TInt size;
	m_fd.Size(size);
    return size;
}

/// Synchronizes the file's data and status with the disk.
void fstream::sync (void)
{
}

} // namespace ustl

